import logging
import secrets
import string
from datetime import datetime, timedelta
from typing import Tuple

import aiofiles
from aiogram.client.session.aiohttp import ClientSession
from dateutil.relativedelta import relativedelta

from src.application.category.dto import CategoryDTO
from src.application.order.enums import OrderPeriod
from src.application.proxy.dto.proxy import ProxyDTO

logger = logging.getLogger(__name__)


async def write_to_file(file_path: str, content: str, mode: str = "w"):
    """Асинхронно пишем в файл"""

    async with aiofiles.open(file_path, "w") as f:
        await f.write(content)


async def get_expiring_on(date: datetime, period: OrderPeriod) -> datetime:
    # TODO Переместить в другое место, переделать
    if period == OrderPeriod.twenty_minutes:
        expiring_on = date + timedelta(minutes=20)
    if period == OrderPeriod.four_hours:
        expiring_on = date + timedelta(hours=4)
    elif period == OrderPeriod.one_day:
        expiring_on = date + timedelta(days=1)
    elif period == OrderPeriod.one_week:
        expiring_on = date + timedelta(days=7)
    elif period == OrderPeriod.two_weeks:
        expiring_on = date + timedelta(days=14)
    elif period == OrderPeriod.one_month:
        expiring_on = date + relativedelta(months=1)

    return expiring_on


async def get_price_by_category(period: OrderPeriod, category: CategoryDTO, selected_amount: int) -> float:
    # TODO Переместить в другое место, переделать
    if period == OrderPeriod.four_hours:
        order_summ = round(category.price_4h * selected_amount, 2)
    elif period == OrderPeriod.one_day:
        order_summ = round(category.price_1d * selected_amount, 2)
    elif period == OrderPeriod.one_week:
        order_summ = round(category.price_1w * selected_amount, 2)
    elif period == OrderPeriod.two_weeks:
        order_summ = round(category.price_2w * selected_amount, 2)
    elif period == OrderPeriod.one_month:
        order_summ = round(category.price_1m * selected_amount, 2)

    return order_summ


async def generate_proxy() -> Tuple[str, str, str]:
    # TODO Перенести в другое место
    alphabet = string.ascii_letters + string.digits
    login = "".join(secrets.choice(alphabet) for _ in range(8))
    password = "".join(secrets.choice(alphabet) for _ in range(8))
    reload_link = secrets.token_urlsafe(16)

    return login, password, reload_link


# async def get_server_proxies(
#     db_session: AsyncSession,
#     proxies: list[Proxy] | list[dict[str, Any]],
#     new_proxies: Optional[list[Proxy]] = None,
# ) -> dict[Server, list[Proxy]] | dict[Server, Tuple[Proxy, Proxy]]:
#     """
#     Returns the dict containing the server and proxy belonging to it.
#     Also can return dict also containing new proxies belonging to server
#     :param AsyncSession db_session: SQLAlchemy session
#     :param List[Proxy] | List[dict[str | Any]] proxies: list of Proxy instances or list of dicts
#     :param Optional[List[Proxy]] new_proxies: list of Proxy instances with new credentials
#     """
#     # TODO Перенести в другое место, переделать
#
#     servers = []
#     server_ids = [proxy.server_id for proxy in proxies]
#     server_ids = list(set(server_ids))
#
#     for server_id in server_ids:
#         server = await get_server(db_session, server_id)
#         servers.append(server)
#
#     server_proxies = {}
#     for server in servers:
#         if new_proxies:
#             # Объединяем в один словарь старые и новые прокси, которые относятся к определенному серверу
#             server_proxies[server] = [
#                 (new_proxy, old_proxy)
#                 for old_proxy, new_proxy in zip(proxies, new_proxies)
#                 if old_proxy.get("server_id") == server.id and new_proxy.server == server
#             ]
#         else:
#             server_proxies[server] = [proxy for proxy in proxies if server == proxy.server]
#
#     return server_proxies


async def get_server_proxies(
    proxies: list[ProxyDTO], new_proxies: list[ProxyDTO] | None = None
) -> dict[int, list[tuple[ProxyDTO, ProxyDTO]] | list[ProxyDTO]]:
    """Получаем словарь с id серверов, которым принадлежат прокси"""

    server_proxies = {}
    server_ids = [proxy.server_id for proxy in proxies]
    server_ids = list(set(server_ids))

    for server_id in server_ids:
        if new_proxies:
            # Объединяем в один словарь старые и новые прокси, которые относятся к определенному серверу
            server_proxies[server_id] = [
                (new_proxy, old_proxy)
                for old_proxy, new_proxy in zip(proxies, new_proxies)
                if old_proxy.server_id == server_id and new_proxy.server_id == server_id
            ]
        else:
            server_proxies[server_id] = [proxy for proxy in proxies if proxy.server_id == server_id]

    return server_proxies


async def get_rate_usd() -> float:
    url = "https://www.cbr-xml-daily.ru/daily_json.js"
    # TODO Перенести в другое место
    try:
        async with ClientSession() as session:
            async with session.get(url, verify_ssl=False) as req_:
                res = await req_.json(content_type=None)
        return res.get("Valute").get("USD").get("Value")
    except Exception as e:
        logger.exception(e)
    return 0
